package com.yunji.cabinet.engine;

import com.google.common.collect.Lists;
import com.yunji.base.cache.BaseCache;
import com.yunji.cabinet.bo.UpgradeInfoRedisBO;
import com.yunji.cabinet.device.DeviceCacheCode;
import com.yunji.cabinet.device.DeviceCommandEngine;
import com.yunji.cabinet.domain.ContainerDO;
import com.yunji.cabinet.domain.UpgradeFileDO;
import com.yunji.cabinet.domain.UpgradeInfoDO;
import com.yunji.cabinet.domain.UpgradeTaskDO;
import com.yunji.cabinet.dto.device.UpgradeCommandDTO;
import com.yunji.cabinet.enums.UpgradeTaskStatus;
import com.yunji.cabinet.service.IUpgradeFileService;
import com.yunji.cabinet.service.IUpgradeInfoService;
import com.yunji.cabinet.service.IUpgradeTaskService;
import com.yunji.common.cache.LockUtil;
import com.yunji.common.constant.LogicCode;
import com.yunji.common.enums.common.ErrorEnum;
import com.yunji.common.utils.TimeUtils;
import com.yunji.common.utils.bean.BeanUtils;
import com.yunji.system.context.SystemUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.yunji.common.constant.LockCacheConstant.CABINET_UPGRADE_LOCK;

@Component
@Slf4j
public class UpgradeTaskEngine {

    @Resource
    private IUpgradeInfoService upgradeInfoService;

    @Resource
    private IUpgradeTaskService upgradeTaskService;

    @Resource
    private IUpgradeFileService upgradeFileService;


    @Async
    public void createTask(UpgradeTaskDO upgradeTaskDO, List<ContainerDO> list) {
        log.info("======================创建升级任务=======================");

        List<UpgradeInfoDO> infoList = new ArrayList<>();
        for (ContainerDO cabinetDO : list) {
            UpgradeInfoDO infoDO = new UpgradeInfoDO();
            BeanUtils.clone(cabinetDO, infoDO);
            infoDO.setTaskCode(upgradeTaskDO.getTaskCode());
            infoDO.setChipType(upgradeTaskDO.getChipType());
            infoDO.setVersion(upgradeTaskDO.getVersion());
            infoDO.setFileId(upgradeTaskDO.getFileId());
            infoDO.setSiteId(cabinetDO.getSiteId());
            infoDO.setSiteName(BaseCache.getSiteName(cabinetDO.getSiteId()));
            infoDO.setAgentId(cabinetDO.getAgentId());
            infoDO.setAgentName(BaseCache.getAgentName(cabinetDO.getAgentId()));
            infoList.add(infoDO);
        }
        upgradeInfoService.saveBatch(infoList);
        // 一个就绪中 其他都是等待中
        String taskCode = upgradeTaskDO.getTaskCode();
        Assert.isTrue(upgradeTaskService.modifyStatus(taskCode, UpgradeTaskStatus.BE_READY.getCode()
                , Lists.newArrayList(UpgradeTaskStatus.TO_BE_START.getCode())), ErrorEnum.BUSINESS_712004.getText());
        Long fileId = upgradeTaskDO.getFileId();
        UpgradeFileDO fileDO = upgradeFileService.select(fileId);
        Assert.isTrue(BeanUtils.isNotNull(fileDO), ErrorEnum.BUSINESS_712005.getText());
        saveCache(infoList);
        startTask(infoList, fileDO, taskCode);
    }

    private void saveCache(List<UpgradeInfoDO> infoList) {
        for (UpgradeInfoDO infoDO : infoList) {
            UpgradeInfoRedisBO bo = new UpgradeInfoRedisBO();
            bo.setReadyTime(TimeUtils.getNowDate().getTime());
            bo.setCurrentId(infoDO.getInfoId());
            SystemUtil.redisCache().set(DeviceCacheCode.DEVICE_UPGRADE_TASK + infoDO.getChipImei(), bo);
        }
    }


    private void startTask(List<UpgradeInfoDO> infoList, UpgradeFileDO fileDO, String taskCode) {
        Assert.isTrue(upgradeTaskService.modifyStatus(taskCode, UpgradeTaskStatus.IN_PROGRESS.getCode()
                , Lists.newArrayList(UpgradeTaskStatus.BE_READY.getCode())), ErrorEnum.BUSINESS_712004.getText());
        upgradeTaskService.startTask(taskCode);
        // 校验当前进行的taskId 是不是缓存中的taskId 不是就改为已取消
        for (UpgradeInfoDO infoDO : infoList) {
            boolean b = LockUtil.lockWithoutThread(CABINET_UPGRADE_LOCK + infoDO.getChipImei(), 40, 80, TimeUnit.SECONDS);
            if (b) {
                // 如果被取消 直接不执行
                if (upgradeInfoService.checkCancel(infoDO.getInfoId())) continue;
                UpgradeInfoRedisBO bo = SystemUtil.redisCache().get(DeviceCacheCode.DEVICE_UPGRADE_TASK + infoDO.getChipImei());
                Long currentId = bo.getCurrentId();
                // 任务不是最新任务则不执行
                if (BeanUtils.isNull(currentId) || !currentId.equals(infoDO.getInfoId())) {
                    LockUtil.unlock(CABINET_UPGRADE_LOCK + infoDO.getChipImei());
                    Assert.isTrue(upgradeInfoService.modifyStatus(infoDO.getInfoId(), UpgradeTaskStatus.TO_BE_START.getCode(), Lists.newArrayList(UpgradeTaskStatus.BE_CANCEL.getCode())));
                    continue;
                }
                // 执行任务等待硬件解锁
                upgradeInfoService.startTask(infoDO.getInfoId());

                UpgradeCommandDTO upgrade = new UpgradeCommandDTO();
                upgrade.setVersion(fileDO.getVersion());
                upgrade.setImei(infoDO.getChipImei());
                upgrade.setInfoId(infoDO.getInfoId());
                upgrade.setFileId(fileDO.getFileId());
                // 如果不在线或者升级失败 则进入定时任务等待执行 无需删除缓存 定时任务再去校验是否最新 需要执行定时任务
                if (!DeviceCommandEngine.upgrade(upgrade)) {
                    upgradeInfoService.modifyWhetherRetry(infoDO.getInfoId(), LogicCode.NO, Lists.newArrayList(LogicCode.YES));
                }

            }
        }
//        // 如果没有等待中的任务详情 说明整个任务都已结束
//        if (!upgradeInfoService.checkWaitInfo(taskCode)) {
//            Assert.isTrue(upgradeTaskService.modifyStatus(taskCode, UpgradeTaskStatus.BE_FINISHED.getCode()
//                    , Lists.newArrayList(UpgradeTaskStatus.IN_PROGRESS.getCode())), ErrorEnum.BUSINESS_712004.getText());
//            upgradeTaskService.closeTask(taskCode);
//        }
    }

    @Async
    public void retryTask(UpgradeTaskDO task, List<UpgradeInfoDO> infoList) {
        Long fileId = task.getFileId();
        UpgradeFileDO fileDO = upgradeFileService.select(fileId);
        Assert.isTrue(BeanUtils.isNotNull(fileDO), ErrorEnum.BUSINESS_712005.getText());
        startTask(infoList, fileDO, task.getTaskCode());
    }
}
